/**
 * 公开认证控制器 - 不需要认证Token的路由处理函数
 */

const jwt = require("jsonwebtoken");
const User = require("../models/userModel");
const MenuModel = require('../models/menuModel');
const PermissionModel = require('../models/permissionModel');
const Token = require("../models/tokenModel");
const {jwtConfig} = require('../../config');
const bcrypt = require("bcryptjs");
const { success, error, serverError, unauthorized, badRequest } = require('../utils/response');
const { validateRequired, validateUsername, validatePassword } = require('../utils/validator');
const { createCaptcha, getCaptchaStatus, clearAllCaptcha } = require('../services/captchaService');
const { recordUserLogin, getClientIP, cleanIP , calculateExpiry} = require('../utils/utils');
const SSEManager = require('../utils/sseManager');
const sseManager = SSEManager.getInstance();
const { getLogger } = require('../logger');
const { logAudit } = require('../utils/auditLogger');
const logger = getLogger('controllers.publicAuthController');
const cacheService = require('../cache/cacheService');
const { generateCacheKey } = require('../cache/utils/cacheKeyGenerator');

// 用户登录
const login = async (req, res) => {
    const { username, password } = req.body;
    
    // 输入验证
    const requiredValidation = validateRequired(req.body, ['username', 'password']);
    if (!requiredValidation.isValid) {
        return badRequest(req, res, requiredValidation.message);
    }

    // 用户名格式验证
    const usernameValidation = validateUsername(username);
    if (!usernameValidation.isValid) {
        return badRequest(req, res, usernameValidation.message);
    }

    // 密码强度验证
    const passwordValidation = validatePassword(password);
    if (!passwordValidation.isValid) {
        return badRequest(req, res, passwordValidation.message);
    }

    // 临时变量，用于存储用户ID
    let briefuser = null;

    try {
        
        // 注意：这里不再使用session验证码，改为使用内存存储的验证码
        // 如果需要验证码验证，请在路由中使用 verifyCaptchaMemory 中间件
        
        const user = await User.findByUsername(username);

        briefuser = Object.assign({}, user);

        if (!user || !bcrypt.compareSync(password, user.password)) {
            return unauthorized(req, res, "用户名或密码错误");
        }
       
        // 生成token，使用jwtUtils导出的密钥，确保安全性
        const accessToken = jwt.sign({ 
            id: user.id,
            username: user.username,
            // 添加随机盐值增强安全性
            jti: Date.now().toString(36) + Math.random().toString(36).substr(2)
        }, jwtConfig.secret, { 
            expiresIn: jwtConfig.expiresIn,
            algorithm: jwtConfig.algorithm,
            notBefore: 0 // 立即生效
        });

        const refreshToken = jwt.sign({ 
            id: user.id,
            username: user.username,
            // 添加随机盐值增强安全性
            jti: Date.now().toString(36) + Math.random().toString(36).substr(2)
        }, jwtConfig.refreshSecret, { 
            expiresIn: jwtConfig.refreshExpiresIn,
            algorithm: jwtConfig.algorithm,
            notBefore: 0 // 立即生效
        });

        // 计算过期时间
        const accessExpires = calculateExpiry(jwtConfig.expiresIn);
        const refreshExpires = calculateExpiry(jwtConfig.refreshExpiresIn);

        // 记录登录信息
        let sessionId = null;
        try {
            const clientIP = cleanIP(getClientIP(req));
            const userAgent = req.headers['user-agent'] || '';
            
            const { logId, sessionId: newSessionId } = await recordUserLogin({
                username: user.username,
                department: user.department_name || '未知部门',
                hostIp: clientIP,
                userAgent: userAgent,
                loginType: 'web'
            });
            
            sessionId = newSessionId;
            logger.info(`用户 ${user.username} 登录成功，记录ID: ${logId}，会话ID: ${sessionId}`);
        } catch (logError) {
            logger.error('记录登录信息失败', { error: logError });
            // 登录记录失败不影响登录流程
        }

        // 写入数据库，关联会话ID
        // 注意：实际存储前可以考虑只存储token的哈希值而不是完整token
        const createaccessToken = await Token.createAccessToken({user_id: user.id, expiresAt: accessExpires, accessToken, session_id: sessionId});
        const createrefreshToken = await Token.createRefreshToken({user_id: user.id, expiresAt: refreshExpires, refreshToken, session_id: sessionId});
        
        if (!createaccessToken || !createrefreshToken) {
            throw new Error('Token创建失败');
        }

        // 单点登录强化：保留本次会话，清退其他会话并通过 SSE 通知
        try {
            if ((process.env.SINGLE_LOGIN || '').toLowerCase() === 'on') {
                await Token.revokeOtherSessionTokens(user.id, sessionId);
                sseManager.sendToUser(user.id, {
                    type: 'loginout',
                    data: {
                        reason: 'single_login_enforced',
                        sessionId,
                        timestamp: new Date().toISOString()
                    }
                });
                logger.info('单点登录已生效，其他会话已清退，并发送SSE通知', { userId: user.id, keepSessionId: sessionId });
            }
        } catch (sseErr) {
            logger.warn('单点登录清退或SSE通知失败', { error: sseErr.message });
        }

        // 构造用户信息（可根据实际表结构调整字段）
        const userinfo = {
            id: user.id,
            is_admin: user.is_admin,
            username: user.username,
            name: user.name,
            mobile: user.mobile,
            avatar: user.avatar_url,
            password: user.password,
            department: user.department_name,
            // roles: user.roles,
            permissions: user.permissions,
            gender: user.gender,
            email: user.email,
            status: user.status,
            create_time: user.create_time,
            update_time: user.update_time,
        };


        // 新逻辑：根据用户绑定的角色聚合菜单和权限
        // 生成用户角色缓存键，符合命名规范
        const cacheKey = generateCacheKey('user', 'roles', `user_${user.id}`);
        
        // 尝试从缓存获取角色数据
        let roles = await cacheService.get(cacheKey);
        
        if (!roles) {
            roles = await User.getUserRoles(user.id);
            await cacheService.set(cacheKey, roles, null);
            logger.info('将用户角色存入缓存', { userId: user.id });
        }
        
        const roleIds = roles.map(r => r.id);

        let menus = [];
        let permissions = [];

        for (const roleId of roleIds) {
            // 生成菜单缓存键，符合命名规范
            const menuCacheKey = generateCacheKey('menu', 'list', `role_${roleId}_user_${user.id}`, 'full');
            
            // 尝试从缓存获取菜单数据
            let roleMenus = await cacheService.get(menuCacheKey);
            
            if (!roleMenus) {
                roleMenus = await MenuModel.getBindMenus(roleId, user.id, true);
                await cacheService.set(menuCacheKey, roleMenus, null, 'userPermissions');
                logger.info('将角色菜单存入缓存', { roleId, userId: user.id });
            } else {
                logger.info('从缓存获取角色菜单', { roleId, userId: user.id });
            }
            
            // 生成权限缓存键，符合命名规范
            const permissionCacheKey = generateCacheKey('permission', 'codes', `role_${roleId}_user_${user.id}`);
            
            // 尝试从缓存获取权限数据
            let rolePermissions = await cacheService.get(permissionCacheKey);
            
            if (!rolePermissions) {
                rolePermissions = await PermissionModel.getPermissionCodesByRoleId(roleId, user.id);
                await cacheService.set(permissionCacheKey, rolePermissions, null, 'userPermissions');
                logger.info('将角色权限存入缓存', { roleId, userId: user.id });
            } else {
                logger.info('从缓存获取角色权限', { roleId, userId: user.id });
            }
            
            menus = menus.concat(roleMenus);
            permissions = permissions.concat(rolePermissions);
        }

        // 去重
        const uniqueMenus = Array.from(new Map(menus.map(m => [m.id, m])).values());
        const uniquePermissions = [...new Set(permissions)];
        
        // 登录成功后，清除briefuser
        briefuser = null;

        await logAudit(req, { action_type: 'login', module_code: 'login', business_type: 'login', operation_desc: 'login', target_entity_type: 'user', target_entity_id: user.id, target_entity_name: user.username ,operation_desc:'1'});
        success(req, res, '登录成功', {
            accessToken, 
            refreshToken,
            userinfo,
            menus: uniqueMenus,
            permissions: uniquePermissions,
            sessionId // 返回会话ID给前端
        });
    } catch (error) {
        // 直接将错误对象作为第一个参数传递，logger会自动处理
        logger.error('登录失败',{ userId: briefuser?.id, username: briefuser?.username }, error);
        await logAudit(req, { action_type: 'login', module_code: 'login', business_type: 'login', operation_desc: 'login', target_entity_type: 'user', target_entity_id: briefuser?.id, target_entity_name: briefuser?.username ,operation_desc:'0'});
        serverError(req, res, '登录失败，请稍后重试');
    }
};

// 健康检查路由
const health = async (req, res) => {
    try {
        const dbHealthy = await Token.healthCheck();
        success(req, res, '服务正常', {
            status: 'OK',
            database: dbHealthy ? 'connected' : 'disconnected',
            timestamp: new Date().toISOString()
        });
    } catch (err) {
        logger.error('数据库健康检查失败', { error: err });
        serverError(req, res, '服务异常，数据库连接失败');
    }
};

// 生成登录验证码（服务层生成，控制器负责响应）
const captchaImage = async (req, res) => {
  try {
    const captcha = await createCaptcha();
    res.type('svg');
    res.set('Cache-Control', 'no-cache, no-store, must-revalidate');
    res.set('Pragma', 'no-cache');
    res.set('Expires', '0');
    success(req, res, '验证码生成成功', {
      captchaId: captcha.id,
      captchaImage: captcha.svg,
      expiresIn: captcha.expiresInSeconds
    });
  } catch (err) {
    logger.error('生成验证码错误:', err);
    serverError(req, res, '生成验证码失败');
  }
};

// 获取验证码状态（开发环境调试用）
const getCaptchaStatusController = async (req, res) => {
  try {
    const status = await getCaptchaStatus();
    success(req, res, '获取验证码状态成功', status);
  } catch (err) {
    logger.error('获取验证码状态错误', { error: err });
    serverError(req, res, '获取验证码状态失败');
  }
};

// 清理所有验证码（开发环境调试用）
const clearCaptcha = async (req, res) => {
  try {
    const clearedCount = await clearAllCaptcha();
    success(req, res, `清理了 ${clearedCount} 个验证码`);
  } catch (err) {
    logger.error('清理验证码错误', { error: err });
    serverError(req, res, '清理验证码失败');
  }
};

// 监控上报
const report = async (req, res) => {
    const { userId } = req.params || {};
    const { reportType, reportContent } = req.body;
    
    // 公开路由的监控上报可能不要求userId，这里调整验证逻辑
    if (!reportType) {
        return badRequest(req, res, '举报类型不能为空');
    }
    if (!reportContent) {
        return badRequest(req, res, '举报内容不能为空');
    }
    
    try {
        // 如果没有提供userId，可以使用null或者其他默认值
        const reportId = await Token.MonitorReport(userId || null, reportType, reportContent);
        success(req, res, '举报成功', { reportId });
    } catch (err) {
        logger.error('举报错误', { error: err });
        serverError(req, res, '举报失败');
    }
};

// SSE 公共连接控制器

// Token刷新控制器

/**
 * 刷新Token（使用refreshToken进行验证，无需accessToken认证）
 */
const refresh = async (req, res) => {
    try {
        const refreshTokenData = req.refreshTokenData;
        const userId = refreshTokenData.user_id;
        
        // 生成新的access token
        const newAccessToken = jwt.sign({ 
            id: userId,
            username: refreshTokenData.username, 
            // 添加随机盐值增强安全性
            jti: Date.now().toString(36) + Math.random().toString(36).substr(2)
        }, jwtConfig.secret, { 
            expiresIn: jwtConfig.expiresIn,
            algorithm: jwtConfig.algorithm,
            notBefore: 0 // 立即生效
        });

        const newRefreshToken = jwt.sign({ 
            id: userId,
            username: refreshTokenData.username, 
            // 添加随机盐值增强安全性
            jti: Date.now().toString(36) + Math.random().toString(36).substr(2)
        }, jwtConfig.refreshSecret, { 
            expiresIn: jwtConfig.refreshExpiresIn,
            algorithm: jwtConfig.algorithm,
            notBefore: 0 // 立即生效
        });

        // 计算过期时间
        const newaccessExpires = calculateExpiry(jwtConfig.expiresIn);
        const newrefreshExpires = calculateExpiry(jwtConfig.refreshExpiresIn);

        // 写入数据库
        const createaccessToken = await Token.createToken({user_id:userId,token:newAccessToken,token_type:'access',expiresAt:newaccessExpires,session_id:refreshTokenData.session_id});
        const createrefreshToken = await Token.createToken({user_id:userId,token:newRefreshToken,token_type:'refresh',expiresAt:newrefreshExpires,session_id:refreshTokenData.session_id});        
        
        success(req, res, 'Token刷新成功', {
            accessToken: newAccessToken,
            refreshToken: newRefreshToken,
            expiresAt: newaccessExpires
        });
        
    } catch (err) {
        logger.error('Token刷新错误', { error: err });
        serverError(req, res, 'Token刷新失败，请重新登录');
    }
};

/**
 * 建立 SSE 连接（无需认证，可用于公开通知）
 */
const connectPublic = (req, res) => {
    try {
        // 创建连接但不关联用户
        const connectionId = sseManager.createConnection(req, res);
        
        logger.info(`公共 SSE 连接已建立: ${connectionId}`);
        // 注意：这里不能关闭响应，因为 SSE 需要保持连接打开
    } catch (error) {
        logger.error('建立公共 SSE 连接失败:', error);
        res.status(500).json({
            success: false,
            message: '建立连接失败',
            error: error
        });
    }
};

// 监控数据相关
const MonitorModel = require('../models/monitorModel');

/**
 * 接收监控数据上报
 */
const reportMonitorData = async (req, res) => {
    try {
        const { sessionId, data, timestamp, url, userid = '' } = req.body;
        
        // 验证必填字段
        if (!data || !Array.isArray(data)) {
            return badRequest(req, res, '数据格式错误，data必须是数组');
        }
        
        if (data.length === 0) {
            return success(req, res, '上报成功', { insertedCount: 0 });
        }

        // 准备上报数据
        const reportData = {
            sessionId: sessionId || `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
            data,
            userId: userid,
            timestamp: timestamp || Date.now(),
            url: url || req.headers.referer || ''
        };
        
        // 批量插入数据库
        const result = await MonitorModel.batchInsertMonitorData(reportData);
        
        // 处理不同的插入结果情况
        if (result.partialSuccess) {
            logger.info(`⚠️  监控数据部分上报成功: ${result.insertedCount}/${data.length} 条`);
            return success(req, res, '部分数据上报成功', {
                insertedCount: result.insertedCount,
                monitorData: result.monitorData,
                behaviorEvents: result.behaviorEvents,
                performanceData: result.performanceData,
                totalCount: data.length,
                sessionId: reportData.sessionId,
                partialSuccess: true,
                message: result.error
            });
        } else {
            logger.info(`✅ 监控数据上报成功: ${result.insertedCount} 条`);
            return success(req, res, '上报成功', {
                insertedCount: result.insertedCount,
                monitorData: result.monitorData,
                behaviorEvents: result.behaviorEvents,
                performanceData: result.performanceData,
                sessionId: reportData.sessionId,
                message: `成功插入 ${result.insertedCount} 条数据`
            });
        }
    } catch (err) {
        logger.error('❌ 监控数据上报失败', { error: err });
        return serverError(req, res, '上报失败');
    }
};

/**
 * 监控健康检查
 */
const monitorHealth = (req, res) => {
    res.json({
        status: 'ok',
        message: '监控服务运行正常'
    });
};

// Sourcemap相关
const fs = require('fs');
const path = require('path');
const SOURCEMAP_DIR = path.join(__dirname, '../data/sourcemaps');

/**
 * 获取sourcemap文件
 */
const getSourcemap = async (req, res) => {
    try {
        // 检查是否处于调试模式
        const isDebugMode = req.query.debug === 'true' || req.headers['x-debug'] === 'true';
        
        if (!isDebugMode) {
            return error(req, res, '调试模式未开启', 403, null);
        }
        
        // 获取请求的文件名
        const filename = req.params.filename;
        
        // 确保文件名只包含合法字符，防止路径遍历攻击
        if (!filename || !/^[a-zA-Z0-9~_\-\.]+\.js\.map$/.test(filename)) {
            return error(req, res, '无效的文件名', 400, null);
        }
        
        // 从请求头获取hash目录
        const hashDir = req.headers['hashpath'];
        
        // 解析文件名，构建目标路径
        let targetPath;
        
        // 如果提供了hashDir，验证其有效性并使用
        if (hashDir) {
            // 验证hashDir只包含合法字符，防止路径遍历攻击
            if (!/^[a-zA-Z0-9～_\-]+$/.test(hashDir)) {
                return error(req, res, '无效的hash目录名', 400, null);
            }
            
            // 构建目标路径
            targetPath = path.join(SOURCEMAP_DIR, hashDir, filename);
            
            // 检查该hash目录下的文件是否存在
            if (!fs.existsSync(path.join(SOURCEMAP_DIR, hashDir))) {
                return error(req, res, `指定的hash目录不存在: ${hashDir}`, 404, null);
            }
        } else {
            // 如果没有提供hashDir，从最新的hash目录中查找
            try {
                const hashDirs = fs.readdirSync(SOURCEMAP_DIR).filter(dir => {
                    return fs.statSync(path.join(SOURCEMAP_DIR, dir)).isDirectory();
                });
                
                if (hashDirs.length === 0) {
                    return error(req, res, '没有找到sourcemap目录', 404, null);
                }
                
                // 按修改时间排序，使用最新的hash目录
                hashDirs.sort((a, b) => {
                    const timeA = fs.statSync(path.join(SOURCEMAP_DIR, a)).mtime.getTime();
                    const timeB = fs.statSync(path.join(SOURCEMAP_DIR, b)).mtime.getTime();
                    return timeB - timeA;
                });
                
                targetPath = path.join(SOURCEMAP_DIR, hashDirs[0], filename);
            } catch (dirError) {
                logger.error('读取sourcemap目录失败', { error: dirError });
                return error(req, res, '读取sourcemap目录失败', 500, null);
            }
        }
        
        // 检查文件是否存在
        if (!fs.existsSync(targetPath)) {
            return error(req, res, 'Sourcemap文件不存在', 404, null);
        }
        
        // 设置正确的MIME类型
        res.setHeader('Content-Type', 'application/json');
        
        // 发送文件
        res.sendFile(targetPath);
    } catch (err) {
        logger.error('获取sourcemap文件失败', { error: err });
        return error(req, res, '服务器内部错误', 500, null);
    }
};

/**
 * 检查调试模式状态
 */
const checkDebugStatus = async (req, res) => {
    try {
        // 从环境变量获取调试模式的默认状态
        const defaultDebugEnabled = process.env.ENABLE_SOURCEMAP_DEBUG === 'true';
        
        // 检查目录是否存在
        const dirExists = fs.existsSync(SOURCEMAP_DIR);
        
        // 准备响应数据
        const response = {
            debugEnabled: defaultDebugEnabled,
            directoryExists: dirExists,
            directoryPath: SOURCEMAP_DIR
        };
        
        // 如果目录存在，添加目录信息
        if (dirExists) {
            try {
                const hashDirs = fs.readdirSync(SOURCEMAP_DIR).filter(dir => {
                    return fs.statSync(path.join(SOURCEMAP_DIR, dir)).isDirectory();
                });
                response.hashDirectories = hashDirs;
                response.hashDirectoryCount = hashDirs.length;
            } catch (readError) {
                logger.error('读取sourcemap目录内容失败', { error: readError });
                response.error = '无法读取目录内容';
            }
        }
        
        return success(req, res, '获取调试状态成功', response);
    } catch (err) {
        logger.error('检查调试状态失败', { error: err });
        return error(req, res, '服务器内部错误', 500, null);
    }
};

module.exports = {
    login,
    report,
    health,
    captchaImage,
    getCaptchaStatus: getCaptchaStatusController,
    clearCaptcha,
    // Token相关
    refresh,
    // SSE相关
    connectPublic,
    // 监控相关
    reportMonitorData,
    monitorHealth,
    // Sourcemap相关
    getSourcemap,
    checkDebugStatus
};